package com.scs.application.modules.dept.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.scs.application.consts.GlobalConsts;
import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.query.QueryContext;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.core.utils.BeanUtils;
import com.scs.application.core.utils.UtilNum;
import com.scs.application.modules.base.entity.Matr;
import com.scs.application.modules.base.mapper.MatrMapper;
import com.scs.application.modules.base.service.MatrUnitService;
import com.scs.application.modules.base.util.MatrPassUtil;
import com.scs.application.modules.dept.entity.OprtUse;
import com.scs.application.modules.dept.entity.OprtUseItem;
import com.scs.application.modules.dept.mapper.OprtUseItemMapper;
import com.scs.application.modules.dept.mapper.OprtUseMapper;
import com.scs.application.modules.dept.request.OprtUseRequest;
import com.scs.application.modules.dept.service.OprtUseItemService;
import com.scs.application.modules.wm.entity.Stock;
import com.scs.application.modules.wm.entity.StockLock;
import com.scs.application.modules.wm.entity.WorkOrderItem;
import com.scs.application.modules.wm.enums.LockBusType;
import com.scs.application.modules.wm.service.StockLockService;
import com.scs.application.modules.wm.service.StockService;
import com.scs.application.modules.wm.service.WorkOrderItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 */
@Service
public class OprtUseItemServiceImpl extends BaseServiceImpl<OprtUseItemMapper, OprtUseItem> implements OprtUseItemService {
    @Autowired
    private StockService stockService;
    @Autowired
    private StockLockService stockLockService;
    @Autowired
    private OprtUseMapper oprtUseMapper;
    @Autowired
    private WorkOrderItemService workOrderItemService;
    @Autowired
    private MatrMapper matrMapper;

    @Autowired
    MatrUnitService matrUnitService;

    @Autowired
    MatrPassUtil matrPassUtil;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveByStockIds(List<String> stockIds, String useId) {
        if (stockIds.isEmpty()) {
            throw new BusinessException("未找到相关库存信息");
        }
        List<Stock> stocks = stockService.getExpStockById(stockIds);
        if (stocks.size() != stockIds.size()) {
            throw new BusinessException("未找到相关库存信息");
        }
        stocks.stream().filter(stock -> !stock.getFlagOmoc()).findAny().ifPresent(stock -> {
            throw new BusinessException(stock.getSn() + "唯一码不是一物一码耗材，不可消耗");
        });

        stocks.stream().filter(stock -> stock.getPackageQty() == 0).findAny().ifPresent(stock -> {
            throw new BusinessException(stock.getSn() + "库存为0,不可进行消耗操作");
        });
        OprtUse oprtUse = oprtUseMapper.selectById(useId);
        if(oprtUse == null) {
            throw new BusinessException("未找到消耗主单，可能已被移出");
        }

        // 锁库存
        List<StockLock> locks = stockLockService.list(Wrappers.<StockLock>query().in("stock_id", stockIds));
        if (locks != null && locks.size() > 0){
            BusinessException.throwError("唯一码无法使用，已被锁定: %s",locks.get(0).getLockComment());
        }

        List<Matr> matrs = matrMapper.selectList(Wrappers.<Matr>query().in("id", stocks.stream().map(Stock::getMatrId).distinct().collect(Collectors.toList())));
        //是否禁用耗材校验
        matrPassUtil.ckeckMatrPass("patientuse", matrs.stream().map(Matr::getId).collect(Collectors.toList()));

        List<OprtUseItem> oprtUseItems = Lists.newArrayListWithCapacity(stocks.size());
        stocks.stream().forEach(stock -> {
            // 新增病人消耗单明细数据
            OprtUseItem oprtUseItem = new OprtUseItem();
            BeanUtils.copyProperties(stock, oprtUseItem);
            oprtUseItem.setDeptOprtUseId(useId).setStockId(stock.getId()).setLineId(stock.getLineId()).setSupplierId(stock.getSupplierId())
                    .setSupplierName(stock.getSupplierName()).setMatrId(stock.getMatrId())
                    .setMatrName(stock.getMatrName()).setMatrSpec(stock.getMatrSpec())
                    .setPackageQty(stock.getPackageQty()).setPackageUnit(stock.getPackageUnit())
                    .setSkuPrice(stock.getSkuPrice()).setSkuQty(stock.getSkuQty()).setSkuUnit(stock.getSkuUnit())
                    .setAmount(stock.getAmount()).setLot(stock.getLot()).setMdate(new Date())
                    .setExpDate(stock.getExpDate()).setMfgDate(stock.getMfgDate())
                    .setSn(stock.getSn()).setRfid(stock.getRfid()).setUdi(stock.getUdi())
                    .setFlagConsignment(stock.getFlagConsignment()).setFlagOmoc(stock.getFlagOmoc())
                    .setCertificateNo(stock.getCertificateNo())
                    .setMfgName(stock.getMfgName())
                    .setFlagUniqueCode(stock.getFlagUniqueCode()).setRemark("病人消耗单").setId(null);

            if (!matrs.isEmpty()) {
                matrs.stream().filter(matr -> stock.getMatrId().equalsIgnoreCase(matr.getId())).findFirst().ifPresent(matr -> {
                    oprtUseItem.setHisCode(matr.getHisCode())
                            .setHisPrice(matr.getHisPrice());
                });
            }
            oprtUseItems.add(oprtUseItem);
        });

        this.saveBatch(oprtUseItems);

        stockLockService.lockBatch(LockBusType.OPRT_USE, useId, oprtUse.getWarehouseId(),"病人消耗单锁库存,单号:" + oprtUse.getBusKey(), stocks.stream().map(Stock::getSn).collect(Collectors.toList()), "OPRT_USE");

        // 修改消耗单总金额
        updateUseAmount(useId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveByWorkIds(List<String> workIds, String useId) {
        if (workIds.isEmpty()){
            throw new BusinessException("没有可用消耗单");
        }
        for (String workId : workIds) {
            saveByWork(workId, useId);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean saveByWork(String workId, String useId) {
        // 新增消耗单明细数据
        WorkOrderItem workOrderItem = workOrderItemService.getById(workId);

        if (workOrderItem == null){
            throw new BusinessException("未找到出库单信息，数据异常");
        }

        // 判断sn是否在库存表存在
        Stock stock = stockService.getStockByUniqueCode(workOrderItem.getSn());
        if (stock == null){
            throw new BusinessException("唯一码在库存表中未找到，不允许冲红");
        }

        OprtUseItem oprtUseItem = new OprtUseItem();
        BeanUtils.copyProperties(stock, oprtUseItem);
        BeanUtils.copyProperties(workOrderItem, oprtUseItem);
        oprtUseItem.setId(null);
        oprtUseItem.setDeptOprtUseId(useId).setStockId(stock.getId()).setLineId(workOrderItem.getLineId()).setSupplierId(stock.getSupplierId())
                .setSupplierName(stock.getSupplierName()).setMatrId(workOrderItem.getMatrId())
                .setMatrName(workOrderItem.getMatrName()).setMatrSpec(workOrderItem.getMatrSpec())
                .setPackageQty(workOrderItem.getPackageQty()).setPackageUnit(workOrderItem.getPackageUnit())
                .setSkuPrice(workOrderItem.getSkuPrice()).setSkuQty(workOrderItem.getSkuQty()).setSkuUnit(workOrderItem.getSkuUnit())
                .setAmount(workOrderItem.getAmount()).setLot(workOrderItem.getLot())
                .setExpDate(workOrderItem.getExpDate()).setMfgDate(workOrderItem.getMfgDate())
                .setSn(workOrderItem.getSn()).setRfid(workOrderItem.getRfid()).setUdi(workOrderItem.getUdi())
                .setFlagConsignment(stock.getFlagConsignment()).setFlagOmoc(stock.getFlagOmoc())
                .setFlagUniqueCode(stock.getFlagUniqueCode()).setRemark("病人消耗单冲红");
        boolean b = super.saveOrUpdate(oprtUseItem);

        // 修改消耗单金额
        updateUseAmount(useId);

        return b;
    }

    /**
     *  修改病人消耗单主表总金额
     * @param useId
     */
    private void updateUseAmount(String useId) {
        Double total = calcUseAmount(useId);

//        OprtUse oprtUse = new OprtUse();
//        oprtUse.setAmount(total).setId(useId);
//        oprtUseMapper.updateById(oprtUse);

        updateFields(
                "dept_oprt_use",
                useId,
                Arrays.asList("amount"),
                Arrays.asList(UtilNum.getDouble(total)),
                true
        );
    }


    /**
     *  计算病人消耗单主表金额
     * @param useId
     * @return
     */
    private Double calcUseAmount(String useId) {
        Double totalAmount = this.getObj(Wrappers.<OprtUseItem>query().select("sum(amount)" ).eq("dept_oprt_use_id" , useId), v -> {
            if (v == null) {
                return 0.0;
            }
            return Double.valueOf(v.toString());
        });
        return totalAmount;
    }

    /**
     * 删除病人消耗单明细，修改病人消耗单总金额
     * @param idList
     * @return
     */
    @Override
    public boolean removeByIds(Collection<? extends Serializable> idList) {
        if(idList.isEmpty()){
            return true;
        }
        List<OprtUseItem> useDtls = this.list(Wrappers.<OprtUseItem>query().in("id", idList));
        String useId = useDtls.stream().findFirst().get().getDeptOprtUseId();
        List<String> stockIds = useDtls.stream().map(o -> o.getStockId()).collect(Collectors.toList());
        if (stockIds.isEmpty()){
            throw new BusinessException("数据异常");
        }

        OprtUse oprtUse = oprtUseMapper.selectById(useId);
        //非冲红单据时，解锁库存
        if(oprtUse.getFlagUseRedType() != 1){
            List<Stock> stockList = stockService.list(Wrappers.<Stock>query().in("id", stockIds));
            for (Stock stock : stockList) {
                // 解锁库存
                stockLockService.unlock(stock.getId());
            }
        }

        boolean b = super.removeByIds(idList);
        // 修改消耗单金额
        updateUseAmount(useId);
        return b;
    }

    @Override
    public List<OprtUseItem> listByOprtCodeAndDept(String inpatientNo, Boolean flagLine, String deptId){
        List<OprtUse> oprtUses = null;
        if (flagLine){
            oprtUses = oprtUseMapper.selectList(Wrappers.<OprtUse>query()
                    .eq("inpatient_no",inpatientNo).eq("dept_id",deptId).eq("flow_status", "end"));
        }else {
            oprtUses = oprtUseMapper.selectList(Wrappers.<OprtUse>query()
                    .eq("inpatient_no",inpatientNo).eq("flow_status", "end"));
        }

        List<String> ids= oprtUses.stream().map(o -> o.getId()).collect(Collectors.toList());
        List<OprtUseItem> oprtUseItems = this.list(Wrappers.<OprtUseItem>query().in("dept_oprt_use_id",ids));
        return oprtUseItems;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveByOprtSn(OprtUseRequest oprtUseRequest) {
        OprtUse use = oprtUseMapper.selectById(oprtUseRequest.getUseId());
        if (use == null ) {
            BusinessException.throwValidFail("单据不存在，请刷新重试");
        }
        if (!GlobalConsts.FLOW_START_VALUE.equals(use.getFlowStatus())) {
            BusinessException.throwValidFail("单据状态非制单，不可操作");
        }
        oprtUseRequest.setFlagUseRedType(use.getFlagUseRedType() == 1 ? "1" : "0");
        // 判断是否为冲红单据
        if ("1".equals(oprtUseRequest.getFlagUseRedType())) {
            useRedByStockIds(oprtUseRequest.getStockIds(), oprtUseRequest.getUseId(), oprtUseRequest.getOprtCode());
        } else {
            saveByStockIds(oprtUseRequest.getStockIds(), oprtUseRequest.getUseId());
        }
    }

    /**
     * 冲红手术消耗
     * @param stockIds
     * @param useId
     * @param oprtCode
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void useRedByStockIds(List<String> stockIds, String useId, String oprtCode) {
        List<OprtUseItem> redOprtUseItems = Lists.newArrayListWithCapacity(stockIds.size());
        List<Stock> stocks = stockService.listStockWithZero(new QueryWrapper<Stock>().in("id", stockIds), null);
        if (CollectionUtils.isEmpty(stocks)){
            throw new BusinessException("该唯一码不存在库存记录，不允许冲红");
        }
        for (Stock stock : stocks)  {
            List<OprtUseItem> oprtUseItems = this.getUseItemBySn(stock.getSn(), oprtCode);
            List<OprtUseItem> oprtUseItemsExist = this.baseMapper.getUseItemRedNotEnd(stock.getSn());
            if (oprtUseItemsExist != null && oprtUseItemsExist.size() > 0) {
                throw new BusinessException("该唯一码%s已存在未审核的冲红记录，不允许冲红",stock.getSn());
            }

            if (oprtUseItems == null || oprtUseItems.size() == 0) {
                throw new BusinessException("未找到" + stock.getSn() + "的消耗记录，不允许冲红");
            }
            OprtUseItem oprtUseItem = oprtUseItems.get(0);
            OprtUseItem redOprtUseItem = new OprtUseItem();
            BeanUtils.copyProperties(oprtUseItem, redOprtUseItem);
            redOprtUseItem.setDeptOprtUseId(useId).setRemark("病人消耗单冲红").setBillId(null).setBillItemId(null).setId(null);
            redOprtUseItems.add(redOprtUseItem);
        }
        super.saveBatch(redOprtUseItems);
        // 修改消耗单金额
        updateUseAmount(useId);
    }

    /**
     * HIS计费冲红
     * @param oprtUseItems
     * @param useId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void useRedByHisUseItems(List<OprtUseItem> oprtUseItems, String useId) {
        List<OprtUseItem> redOprtUseItems = Lists.newArrayListWithCapacity(oprtUseItems.size());
        for (OprtUseItem oprtUseItem : oprtUseItems) {
            OprtUseItem redOprtUseItem = new OprtUseItem();
            BeanUtils.copyProperties(oprtUseItem, redOprtUseItem);
            redOprtUseItem.setDeptOprtUseId(useId).setRemark("HIS计费冲红").setBillId(null).setBillItemId(null).setId(null);
            redOprtUseItems.add(redOprtUseItem);
        }
        super.saveBatch(redOprtUseItems);
        // 修改消耗单金额
        updateUseAmount(useId);
    }

    @Override
    public IPage<OprtUseItem> pageForRedList(QueryContext queryContext) {
        return baseMapper.pageForRedList(queryContext.getPage(), queryContext.getWrapper(), queryContext.getParams());
    }

    @Override
    public List<OprtUseItem> listForRedList(QueryWrapper wrapper) {
        return baseMapper.listForRedList(wrapper);
    }

    public List<OprtUseItem> getUseItemBySn(String sn, String oprtCode) {
        return baseMapper.getUseItemBySn(sn, oprtCode);
    }
}
